/*
 * @Description: 管理后台用户API
 * @Author: Junhuang
 * @Date: 2022-04-01 14:45:56
 * @LastEditTime: 2022-05-29 18:55:38
 * @LastEditors: Junhuang
 * @Reference:
 */
'use strict';

const Controller = require('egg').Controller;

// 参数校验规则
const rules = {
  username: { type: 'string', required: true, desc: '用户名' },
  name: { type: 'string', required: true, desc: '昵称' },
  no: { type: 'string', required: true, desc: '工号' },
  avatar: { type: 'string', required: true, desc: '头像' },
  rkeys: { type: 'string', required: true, desc: '角色' },
  status: { type: 'int', required: true, desc: '状态' },
  department_id: { type: 'int', required: true, desc: '部门id' },
};

class AdminController extends Controller {
  async login() {
    const { ctx, app, service } = this;

    // 参数验证
    ctx.validate({
      username: { type: 'string', required: true, desc: '用户名' },
      password: { type: 'string', required: true, desc: '密码' },
    });

    // 验证用户是否存在
    const { username, password } = ctx.request.body;

    // 用户名是否存在
    let user = await app.model.Admin.findOne({
      where: {
        username,
      },
    });
    if (!user) {
      ctx.throw(400, '用户不存在或被禁用');
    }

    // 验证密码是否正确
    const cres = service.user.checkPassword(password, user.password);
    if (!cres) {
      ctx.throw(400, '密码不匹配或错误');
    }

    // 特别注意，由于通过model返回对user对象，并非单纯的user对象，还含有许多其他内容，因此在进行token使用时建议进行转换
    user = JSON.parse(JSON.stringify(user));

    // 生成token
    const token = ctx.getToken(user);
    user.token = token;
    delete user.password;

    // 加入缓存
    if (!await this.service.cache.set('admin_' + user.id, token)) {
      ctx.throw(400, '登录失败-redis缓存错误');
    }
    ctx.apiSuccesss({ token });
    // ctx.apiSuccesss('测试错误', 'fail');
  }

  // 退出登录
  async logout() {
    const { ctx, service } = this;
    // 拿到当前用户id
    const c_user_id = ctx.authAdmin.id;
    // 移除redis中当前用户信息
    if (!await service.cache.remove('admin_' + c_user_id)) {
      ctx.throw(400, '退出登录失败-redis异常');
    }
    ctx.apiSuccesss('登出成功');
  }

  async user_info() {
    const { ctx, app } = this;
    const user_id = ctx.authAdmin.id;
    let user = await app.model.Admin.findByPk(user_id);
    if (!user) {
      ctx.throw(400, '用户不存在或被禁用');
    }
    user = JSON.parse(JSON.stringify(user));

    delete user.password;
    ctx.apiSuccesss(user);
  }

  // 访问地址：http://127.0.0.1:7001/admin/list?page=1&limit=10
  async list() {
    const { ctx, app } = this;
    const where = {};
    const Op = app.Sequelize.Op;
    // const sequelize = app.model;
    // 组织查询条件
    const { id, no, status, username, name, department_id, department_name, roles, deep, sort } = ctx.query;
    // id用全等
    if (id && id.length > 0) {
      where.id = parseInt(id);
    }
    // no用全等
    if (no && no.length > 0) {
      where.no = no;
    }

    // stauts全等
    if (status) {
      where.status = parseInt(status);
    }
    const dept_where = {};
    // 注意这里参数传递的是字符串
    if (deep === 'true') {
      dept_where.id_path = {
        [Op.like]: '%,' + department_id + ',%',
      };
    } else {
      if (department_id) {
        where.department_id = parseInt(department_id);
      }
    }

    // username用like
    if (username && username.length > 0) {
      where.username = {
        [Op.like]: '%' + username + '%',
      };
    }

    // name用like
    if (name && name.length > 0) {
      where.name = {
        [Op.like]: '%' + name + '%',
      };
    }

    // roles 用like
    if (roles && roles.length > 0) {
      where.roles = {
        [Op.like]: '%' + roles + '%',
      };
    }

    // 【特别注意】department_name 是子表中的
    if (department_name && department_name.length > 0) {
      dept_where.name = {
        [Op.like]: '%' + department_name + '%',
      };
    }

    const options = {
      include: [{
        model: app.model.Department,
        as: 'department',
        attributes: [[ 'name', 'department_name' ], 'id_path', 'name_path' ],
        where: dept_where,
      }],
      // order: [[ app.model.Department, 'id_path', 'asc' ]],
    };
    // 组织排序
    // 将使用关联名称按关联模型的 createdAt 排序.
    // ['Task', 'createdAt', 'DESC'],
    // 这里排序还需要注意，一定要增加一个主键的排序作为排序的备选，否则容易出现某些数据重复出现在多页的问题
    if (sort && sort.length > 0) {
      const direction = sort.slice(0, 1) === '+' ? 'ASC' : 'DESC';
      const sortColumn = sort.slice(1);
      const index = sortColumn.indexOf('.');
      if (index >= 0) {
        const tb = sortColumn.slice(0, index);
        const colunm = sortColumn.slice(index + 1);
        options.order = [[ tb, colunm, direction ], [ 'id', 'DESC' ]];
      } else {
        options.order = [[ sortColumn, direction ], [ 'id', 'DESC' ]];
      }
    }
    const res = await ctx.pageAndCount(app.model.Admin, where, options);
    const items = [];
    if (res && res.count && res.count > 0) {
      res.rows.forEach(el => {
        const admin = JSON.parse(JSON.stringify(el));
        admin.department_name = admin.department.department_name;
        delete admin.department;
        items.push(admin);
      });

      await Promise.all(
        res.rows.map(async el => {
          const myroles = await el.getMyroles();
          const strRoles = await this.getRolesStr(myroles);
          const temp = items.find(item => {
            return item.id === el.id;
          });
          if (temp) {
            temp.rids = strRoles.ids;
            temp.rkeys = strRoles.keys;
          }
        })
      );
    }


    ctx.apiSuccesss({
      total: res.count,
      items,
    });
  }

  // 获取特定角色下的用户，分页
  // 路由(动态)：‘/role/:id/adminList’
  async getAdminsByRole() {
    const { ctx, app } = this;
    const where = {};
    const Op = app.Sequelize.Op;
    // id为角色id
    const { id } = ctx.params;
    // 组织查询条件
    const { no, name, department_name } = ctx.query;

    // no用全等
    if (no && no.length > 0) {
      where.no = no;
    }

    const dept_where = {};
    // 【特别注意】department_name 是子表中的
    if (department_name && department_name.length > 0) {
      dept_where.name = {
        [Op.like]: '%' + department_name + '%',
      };
    }

    // name用like
    if (name && name.length > 0) {
      where.name = {
        [Op.like]: '%' + name + '%',
      };
    }

    const options = {
      include: [{
        model: app.model.Department,
        as: 'department',
        attributes: [[ 'name', 'department_name' ], 'id_path', 'name_path' ],
        where: dept_where,
        order: [[ 'id_path', 'asc' ]],
      }, {
        model: app.model.Role,
        as: 'myroles',
        where: {
          id: parseInt(id),
        },
      }],
    };

    const res = await ctx.pageAndCount(app.model.Admin, where, options);
    const items = [];
    if (res && res.count && res.count > 0) {
      res.rows.forEach(el => {
        const admin = JSON.parse(JSON.stringify(el));
        admin.department_name = admin.department.department_name;
        delete admin.department;
        delete admin.myroles;
        items.push(admin);
      });

      await Promise.all(
        res.rows.map(async el => {
          const myroles = await el.getMyroles();
          const strRoles = await this.getRolesStr(myroles);
          const temp = items.find(item => {
            return item.id === el.id;
          });
          if (temp) {
            temp.rids = strRoles.ids;
            temp.rkeys = strRoles.keys;
          }
        })
      );
    }
    ctx.apiSuccesss({
      total: res.count,
      items,
    });
  }

  // 新增管理员
  async save() {
    const { ctx, app } = this;
    const { id, username, password, name, no, rkeys, avatar, introduction, status, department_id } = ctx.request.body;
    // const user = ctx.authAdmin;
    // 参数验证
    ctx.validate({
      password: { type: 'string', required: true, desc: '密码' },
      ...rules,
    });
    if (id > 0) {
      return ctx.apiFail('新增时ID应该为0');
    }
    const Op = app.Sequelize.Op;
    const dup_admin = await app.model.Admin.findAll({
      where: {
        [Op.or]: [{ username }, { no }],
      },
    });

    if (dup_admin && dup_admin.length > 0) {
      return ctx.apiFail('用户名或工号重复');
    }

    const myroles = await app.model.Role.findAll({
      where: {
        key: {
          [Op.in]: rkeys.split(','),
        },
      },
    });
    let transaction;
    let admin;
    try {
      transaction = await this.ctx.model.transaction();
      admin = await app.model.Admin.create({
        username, password, name, no, avatar, introduction, status, department_id,
      }, { transaction });
      await admin.addMyroles(myroles, { transaction });
      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      return ctx.apiFail('新增用户到该角色失败' + error);
    }

    // 对返回数据进行整理
    let res = await app.model.Admin.findOne({
      include: [{
        model: app.model.Department,
        as: 'department',
      }],
      where: {
        id: admin.id,
      },
    });
    const tempRoles = await res.getMyroles();
    const strRoles = await this.getRolesStr(tempRoles);
    res = JSON.parse(JSON.stringify(res));
    delete res.password;
    res.department_name = res.department.name;
    res.rids = strRoles.ids;
    res.rkeys = strRoles.keys;
    delete res.department;
    return ctx.apiSuccesss(res);
  }

  // 路由(动态)：‘/admin/:id/roles’
  async getRolesByAdmin() {
    const { ctx, app } = this;
    const admin = await app.model.Admin.findOne({
      where: {
        id: ctx.params.id,
      },
      include: [{
        model: app.model.Role,
        as: 'myroles',
      }],
    });
    if (!admin) {
      return ctx.apiFail('用户不存在');
    }
    ctx.apiSuccesss(await this.getRolesStr(admin.myroles));
  }

  async getRolesStr(roles) {
    const result = {
      ids: '',
      keys: '',
    };
    if (roles.length > 0) {
      result.ids = result.keys = ',';
      roles.forEach(item => {
        result.ids += item.id + ',';
        result.keys += item.key + ',';
      });
    }
    return result;
  }

  // 更新管理员
  // 路由(动态)：‘/admin/:id’
  async update() {
    const { ctx, app } = this;
    let { ucolums, rkeys } = ctx.request.body;
    const colums = {};
    if (!ucolums || ucolums.length <= 0) {
      ucolums = [ 'username', 'name', 'no', 'avatar', 'introduction', 'status', 'department_id' ];
    }
    ucolums.forEach(item => {
      colums[item] = ctx.request.body[item];
    });
    // 【特别注意】：对参数的获取一定要在ctx.validate()之前，否则会导致参数丢失
    ctx.validate({
      id: {
        required: true,
        type: 'int',
        desc: '管理员id',
      },
      ...rules,
    });

    const admin = await app.model.Admin.findOne({
      where: {
        id: ctx.params.id,
      },
    });

    if (!admin) {
      return ctx.throw(404, '该记录不存在');
    }

    const Op = app.Sequelize.Op;
    const myroles = await app.model.Role.findAll({
      where: {
        key: {
          [Op.in]: rkeys.split(','),
        },
      },
    });

    let transaction;
    try {

      transaction = await this.ctx.model.transaction();
      await admin.update({
        ...colums,
      }, { transaction });
      await admin.setMyroles(myroles, { transaction });
      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      return ctx.apiFail('新增用户到该角色失败' + error);
    }
    let res = await app.model.Admin.findOne({
      include: [{
        model: app.model.Department,
        as: 'department',
      }],
      where: {
        id: ctx.params.id,
      },
    });

    // 对返回数据进行整理
    const tempRoles = await res.getMyroles();
    const strRoles = await this.getRolesStr(tempRoles);
    res = JSON.parse(JSON.stringify(res));
    delete res.password;
    res.department_name = res.department.name;
    res.rids = strRoles.ids;
    res.rkeys = strRoles.keys;
    delete res.department;

    return ctx.apiSuccesss(res);
  }

  // 将管理员设置为可用状态，包括将草稿状态=》可用状态 及 禁用状态=》可用状态
  async enable() {
    const { ctx, app } = this;
    ctx.validate({
      id: {
        required: true,
        type: 'int',
        desc: '管理员id',
      },
    });

    const admin = await app.model.Admin.findOne({
      where: {
        id: ctx.params.id,
      },
    });

    if (!admin) {
      return ctx.throw(404, '该管理员不存在');
    }

    if (admin.status === 1) {
      return ctx.throw('该管理员已经为正常状态，不需要此操作！');
    }

    let res = await admin.update({
      status: 1,
    });

    const department = admin.getDepartment();
    console.log('department=', JSON.stringify(department));
    res = JSON.parse(JSON.stringify(res));
    if (department && department.id && department.name) {
      res.department_id = department.id;
      res.department_name = department.name;
    }
    delete res.password;

    return ctx.apiSuccesss(res);
  }

  // 将管理员设置为可用状态，包括将草稿状态=》可用状态 及 禁用状态=》可用状态
  async disable() {
    const { ctx, app } = this;
    ctx.validate({
      id: {
        required: true,
        type: 'int',
        desc: '管理员id',
      },
    });

    const admin = await app.model.Admin.findOne({
      where: {
        id: ctx.params.id,
      },
    });

    if (!admin) {
      return ctx.throw(404, '该管理员不存在');
    }

    if (admin.status !== 1) {
      return ctx.throw('非正常状态管理员无法禁用！');
    }

    let res = await admin.update({
      status: -1,
    });

    const department = await admin.getDepartment();
    res = JSON.parse(JSON.stringify(res));
    if (department && department.id && department.name) {
      res.department_id = department.id;
      res.department_name = department.name;
    }
    delete res.password;

    return ctx.apiSuccesss(res);
  }

  // 删除
  async destroy() {
    const { ctx, app } = this;
    ctx.validate({
      id: {
        required: true,
        type: 'int',
        desc: '管理员id',
      },
    });
    const admin = await app.model.Admin.findOne({
      where: {
        id: ctx.params.id,
      },
    });

    if (!admin) {
      return ctx.throw('该管理员不存在！');
    }

    const res = await admin.destroy();
    ctx.apiSuccesss(`成功删除【id:${res.id},username:${res.username}】的管理员`);
  }

  // 重置密码
  async resetPassword() {
    const { ctx, app } = this;
    ctx.validate({
      id: {
        required: true,
        type: 'int',
        desc: '管理员id',
      },
    });

    const admin = await app.model.Admin.findOne({
      where: {
        id: ctx.params.id,
      },
    });

    if (!admin) {
      return ctx.throw(404, '该管理员不存在');
    }

    await admin.update({
      password: '123456',
    });

    return ctx.apiSuccesss('密码已重置！');
  }

  // 修改密码
  async changePassword() {
    const { ctx, app, service } = this;
    const user_id = ctx.authAdmin.id;
    const { oldPWD, newPWD, newPWD2 } = ctx.request.body;

    // 参数验证
    ctx.validate({
      id: {
        required: true,
        type: 'int',
        desc: '管理员id',
      },
      oldPWD: { type: 'string', required: true, desc: '原密码' },
      newPWD: { type: 'string', required: true, desc: '新密码' },
      newPWD2: { type: 'string', required: true, desc: '确认密码' },
    });

    // console.log(ctx.params.id, user_id);
    if (ctx.params.id !== user_id) {
      return ctx.throw(400, '只能修改自己的密码！');
    }
    // 验证用户是否存在
    const admin = await app.model.Admin.findOne({
      where: {
        id: user_id,
      },
    });

    if (newPWD !== newPWD2) {
      return ctx.throw(400, '新密码与确认密码不一致！');
    }

    if (!admin) {
      return ctx.throw(404, '该管理员不存在');
    }

    // 验证密码是否正确
    const cres = service.user.checkPassword(oldPWD, admin.password);
    if (!cres) {
      ctx.throw(400, '原密码不匹配或错误');
    }

    await admin.update({
      password: newPWD,
    });

    return ctx.apiSuccesss('密码更新成功！');
  }

  // 移除对应用户所属的特定角色
  async removeRole() {
    const { ctx, app } = this;
    const { rkey } = ctx.request.body;
    // 参数验证
    ctx.validate({
      id: {
        required: true,
        type: 'int',
        desc: '管理员id',
      },
      rkey: { type: 'string', required: true, desc: '角色Key' },
    });

    // 验证用户是否存在
    const admin = await app.model.Admin.findByPk(ctx.params.id);
    const role = await app.model.Role.findOne({
      where: {
        key: rkey,
      },
    });

    if (!admin) {
      return ctx.throw(404, '该管理员不存在');
    }

    if (!role) {
      return ctx.throw(404, '该角色不存在');
    }

    await admin.removeMyrole(role);

    return ctx.apiSuccesss(`${admin.name}:${admin.id},移除角色${rkey}成功`);
  }

  // 为指定的多个管理员增加特定角色
  async addRoleByAdmins() {
    const { ctx, app } = this;
    let { ids, rkey } = ctx.request.body;
    if (ids.endsWith(',')) {
      ids = ids.slice(0, ids.length - 1);
    }
    // 参数验证
    ctx.validate({
      ids: { type: 'string', required: true, desc: 'id列表' },
      rkey: { type: 'string', required: true, desc: '角色Key' },
    });

    const Op = app.Sequelize.Op;

    // 验证用户是否存在;
    const admins = await app.model.Admin.findAll({
      where: {
        id: {
          [Op.in]: ids.split(','),
        },
      },
    });

    if (!admins || admins.length === 0) {
      return ctx.throw(404, '该管理员不存在');
    }

    const role = await app.model.Role.findOne({
      where: {
        key: rkey,
      },
    });

    if (!role) {
      return ctx.throw(404, '该角色不存在');
    }

    let names = '';
    admins.forEach(item => {
      names += item.name + ',';
    });

    let transaction;
    try {
      transaction = await this.ctx.model.transaction();
      await role.addAdmins(admins, { transaction });
      await transaction.commit();
      return ctx.apiSuccesss('新增用户：' + names + '到角色[' + rkey + ']成功');
    } catch (err) {
      await transaction.rollback();
      return ctx.apiFail('新增用户到该角色失败' + err);
    }
  }

  // 判断某字段不重复
  async isDuplicated() {
    const { ctx, app } = this;
    const { key, value } = ctx.query;
    ctx.validate({
      id: {
        required: true,
        type: 'int',
        desc: '管理员id',
      },
      key: {
        type: 'string',
        required: true,
        desc: '需判断重复的字段名不能为空',
      },
      value: {
        type: 'string',
        required: true,
        desc: '需判断重复的字段值不能为空',
      },
    });
    const Op = app.Sequelize.Op;
    const where = {};
    where[key] = value;
    const role = await app.model.Admin.findOne({
      where: {
        id: {
          [Op.ne]: ctx.params.id,
        },
        ...where,
      },
    });

    if (role) {
      return ctx.apiFail('此用户' + key + '重复！');
    }
    return ctx.apiSuccesss('此用户' + key + '合法！');
  }

  async testAdminAssociation() {
    const { app, ctx } = this;
    // 注意，如果要使用别名，一定要先在model.Admin中先定义该关联的别名，这里才能使用此别名
    // Admin.belongsTo(app.model.Department, {
    //   foreignKey: 'department_id',
    //   as: 'department',
    // });
    const admin = await app.model.Admin.findOne({
      include: [{
        model: app.model.Department,
        as: 'department',
      }],
    });
    // admin.department = await admin.getDepartment();
    // admin.department = dept;

    // 使用别名后，则其下对象也变为了别名
    admin.department.description += '11111';
    admin.department.save();
    ctx.apiSuccesss(admin.department);
  }

  // 测试role与admin的关系
  async testRoleAdmin() {
    const { app, ctx } = this;
    const admin = await app.model.Admin.findAll({
      include: [{
        model: app.model.Role,
        as: 'myroles',
      }, {
        model: app.model.Department,
        as: 'department',
        attributes: [[ 'name', 'department_name' ], 'id_path', 'name_path' ],
      }],
      order: [[ 'department', 'id_path' ]],
      limit: 10,
      offset: 0,
    });
    ctx.apiSuccesss(admin);
  }

  // 将用户加入到角色中
  async testAddAdminsToRole() {
    const { app, ctx } = this;
    // const Op = app.Sequelize.Op;
    const admis = await app.model.Admin.findAll({
      where: {
        id: [ 3, 6, 7 ],
      },
    });
    const role = await app.model.Role.findByPk(1);
    // 【注意】：这里的hasAdmins,是必须包含所有才返回ture,比如:此处如果该角色只包含部分admin,而不是全部，则返回false.
    const flag = await role.hasAdmins(admis);
    console.log('role.hasAdmins=', flag);
    // addAdmins方法，可以将admins增加到对应角色中，result的结果是影响记录(因为这里是belongsToMany关系，因此返回的是中间表sys_role_admin的影响结果)
    const result = await role.addAdmins(admis);

    // const roles = await admin.getMyroles();
    ctx.apiSuccesss(result);
  }
}

module.exports = AdminController;
